package com.spring.mvc.util;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.SocketException;

import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.spring.mvc.exception.FTPClientException;

/**
 * 操作Ftp工具类
 * @author liujian
 *
 */
public class FtpUtil {

	protected final static Logger logger=LoggerFactory.getLogger(FtpUtil.class);
	
	private String host;
	private int port=21;
	private String userName;
	private String passWord;
	
	private boolean binaryTransfer=true;
	private boolean passiveMode=true;
	private String encoding="UTF-8";
	private int clientTimeOut=3000;
	private String serverDirectory="/";//ftp服务器当前目录
	private boolean isChangeDir=false;
	
	public FtpUtil(String host, String userName, String passWord) {
		this.host = host;
		this.userName = userName;
		this.passWord = passWord;
	}



	public FtpUtil(String host, int port, String userName, String passWord) {
		this.host = host;
		this.port = port;
		this.userName = userName;
		this.passWord = passWord;
	}

	/**
	 * 返回ftp工作空间路径
	 * @return
	 */
	public String getServerDirectory() {
		return serverDirectory;
	}
	/**
	 * 设置Ftp工作空间路径
	 * @param serverDirectory
	 */
	public void setServerDirectory(String serverDirectory) {
		if(!serverDirectory.endsWith("/")){
			serverDirectory = serverDirectory+"/";
		}
		if(!serverDirectory.startsWith("/")){
			serverDirectory = "/"+serverDirectory;
		}
		this.serverDirectory=serverDirectory;
	}
	/**
	 * 是否改变工作空间
	 * @return
	 */
	public boolean isChangeDir() {
		return isChangeDir;
	}
	public void setChangeDir(boolean isChangeDir) {
		this.isChangeDir = isChangeDir;
	}
	public String getHost() {
		return host;
	}
	public void setHost(String host) {
		this.host = host;
	}
	public int getPort() {
		return port;
	}
	public void setPort(int port) {
		this.port = port;
	}
	public String getUserName() {
		return userName;
	}
	public void setUserName(String userName) {
		this.userName = userName;
	}
	public String getPassWord() {
		return passWord;
	}
	public void setPassWord(String passWord) {
		this.passWord = passWord;
	}
	public boolean isBinaryTransfer() {
		return binaryTransfer;
	}
	public void setBinaryTransfer(boolean binaryTransfer) {
		this.binaryTransfer = binaryTransfer;
	}
	public boolean isPassiveMode() {
		return passiveMode;
	}
	public void setPassiveMode(boolean passiveMode) {
		this.passiveMode = passiveMode;
	}
	public String getEncoding() {
		return encoding;
	}
	public void setEncoding(String encoding) {
		this.encoding = encoding;
	}
	public int getClientTimeOut() {
		return clientTimeOut;
	}
	public void setClientTimeOut(int clientTimeOut) {
		this.clientTimeOut = clientTimeOut;
	}
	
    /**  
     * 返回一个FTPClient实例  
     * @throws FTPClientException 
     *   
     * @throws FTPClientException  
     */  
	private FTPClient getFtpClient() throws FTPClientException{
		FTPClient ftpClient=new FTPClient();//构造一个FtpClient实例 
		ftpClient.setControlEncoding(encoding);//设置字符集 
		
		connect(ftpClient);
		//设置为passive模式  
		if(passiveMode){
			ftpClient.enterLocalPassiveMode();
		}
		setFileType(ftpClient);
		
		try {
			//切换到服务器当前目录，此目录是ftp的当前绝对路径
			//是否使用相对路径，被设置后上传下载可用相对路径
			if(isChangeDir){
				ftpClient.changeWorkingDirectory(serverDirectory);
			}
			ftpClient.setSoTimeout(clientTimeOut);
		} catch (SocketException e) {
			throw new FTPClientException("Set timeout error:",e);
		} catch (IOException e) {
			throw new FTPClientException("Set changeWorkingDirectory error:", e);
		}
		return ftpClient;
	}
	
	/**
	 * 设置文件传输类型
	 * @param ftpClient
	 * @throws FTPClientException 
	 */
	private void setFileType(FTPClient ftpClient) throws FTPClientException {
		try {
			if(binaryTransfer){
				ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
			}else{
				ftpClient.setFileType(FTPClient.ASCII_FILE_TYPE);
			}
		} catch (IOException e) {
			throw new FTPClientException("Could not to set file type.", e);
		}
	}
	
	/**
	 * 连接到ftp服务器  
	 * @param ftpClient
	 * @return 连接成功返回true ,否则返回false
	 * @throws FTPClientException
	 */
	private boolean connect(FTPClient ftpClient) throws FTPClientException {
		try {
			ftpClient.connect(host, port);
			 // 连接后检测返回码来校验连接是否成功  
			int reply=ftpClient.getReplyCode();
			if(FTPReply.isPositiveCompletion(reply)){
				//登录到ftp服务器
				if(ftpClient.login(userName, passWord)){
					setFileType(ftpClient);
					return true;
				}
			}else{
				ftpClient.disconnect();
				throw new FTPClientException("FTP server refused connection.");
			}
		} catch (SocketException e) {
			e.printStackTrace();
		} catch (IOException e) {
			if(ftpClient.isConnected()){
				try {
					ftpClient.disconnect();
				} catch (IOException e1) {
					throw new FTPClientException("Could not disconnect form server." , e);
				}
			}
			throw new FTPClientException("Could not connect to server",e);
		}
		return false;
	}
	
	/**
	 * 断开Ftp连接
	 * @param ftpClient
	 * @throws FTPClientException
	 */
	private void disconnect(FTPClient ftpClient) throws FTPClientException{
		try {
			ftpClient.logout();
			if(ftpClient.isConnected()){
				ftpClient.disconnect();
			}
		} catch (IOException e) {
			throw new FTPClientException("Could not disconnect from server.", e); 
		}
	}
	
	/**
	 * 上传一个本地文件到远程指定文件  
	 * @param serverFile 服务器端文件名(包括完整路径)  
	 * @param localFile 本地文件名(包括完整路径)  
	 * @return 成功时，返回true，失败返回false 
	 * @throws FTPClientException 
	 */
	public boolean put(String serverFile,String localFile) throws FTPClientException{
		return put(serverFile,localFile,false);
	}
	
	/**
	 * 上传一个本地文件到远程指定文件  
	 * 
	 * @param serverFile 服务器端文件名(包括完整路径)
	 * @param localFile 本地文件名(包括完整路径)  
	 * @param delFile 是否删除本地文件
	 * @return 成功时，返回true，失败返回false 
	 * @throws FTPClientException
	 */
	public boolean put(String serverFile, String localFile, boolean delFile) throws FTPClientException {
		FTPClient ftpClient=null;
		InputStream input=null;
		try {
			ftpClient=getFtpClient();
			//处理传输
			input=new FileInputStream(localFile);
			ftpClient.storeFile(serverFile, input);
			logger.debug("put "+ localFile);
			if(delFile){
				(new File(localFile)).delete();
			}
			logger.debug("delete " + localFile);
			return true;
		} catch (FileNotFoundException e) {
			throw new FTPClientException("local File not found ." , e);
		} catch (IOException e) {
			throw new FTPClientException("Could not put file to server." , e);
		} finally{
			if(null != input){
				try {
					input.close();
				} catch (IOException e) {
					throw new FTPClientException("Couldn't close FileInputStream." , e);
				}
			}
			if(null != ftpClient){
				disconnect(ftpClient);
			}
		}
	}
	

	/**
	 * 下载一个远程文件到指定的流  
	 * 处理完后记得关闭流 
	 * 
	 * @param serverFile
	 * @param localFile
	 * @return
	 * @throws FTPClientException
	 */
	public boolean get(String serverFile,String localFile) throws FTPClientException{
		return get(getPath(serverFile),localFile,false);
	}
	
	/**
	 * 下载一个远程文件到指定的流 
	 * 处理完后记得关闭流
	 * 
	 * @param serverFile
	 * @param localFile
	 * @param delFile
	 * @return
	 * @throws FTPClientException
	 */
	public boolean get(String serverFile,String localFile,boolean delFile) throws FTPClientException{
		createDir(localFile,false);//判断本地目录是否存在，不存在则创建
		OutputStream output=null;
		try {
			output=new FileOutputStream(localFile);
			if(!get(getPath(serverFile),output,delFile)){
				File file=new File(localFile);
				if(file.exists()){
					file.deleteOnExit();
					return false;
				}
			}
			return true;
		} catch (FileNotFoundException e) {
			throw new FTPClientException("local File not found." , e);
		} finally{
			if(null != output){
				try {
					output.close();
				} catch (IOException e) {
					throw new FTPClientException("Couldn't close FileOutputStream ." , e);
				}
			}
		}
	}
	
	/**
	 * 判断本地(windows)目录是否存在，不存在则创建
	 * @param isDir 目录为true,文件为false
	 * @param path
	 */
	public boolean createDir(String path,boolean isDir){
		if(!isDir){
			path=path.substring(0, path.lastIndexOf(File.separator)+1);
		}else if(!path.endsWith(File.separator)){
			path+=File.separator;
		}
		File file=new File(path);
		if(!file.exists()){
			file.mkdirs();
			return true;
		}
		return false;
	}
	
	/**
	 * 下载一个远程文件到指定的流  
	 * 处理完后记得关闭流  
	 * 
	 * @param serverFile
	 * @param output
	 * @param delFile
	 * @return
	 * @throws FTPClientException
	 */
	public boolean get(String serverFile, OutputStream output, boolean delFile) throws FTPClientException {
		FTPClient ftpClient=null;
		try {
			ftpClient=getFtpClient();
			if(ftpClient.retrieveFile(getPath(serverFile), output)){
				if(delFile){// 删除远程文件 
					ftpClient.deleteFile((serverFile));
				}
			}else{
				return false;
			}
			return true;
		} catch (IOException e) {
			throw new FTPClientException("Couldn't get file from server." , e);
		} finally{
			if(null != ftpClient){
				disconnect(ftpClient);//断开连接  
			}
		}
	}
	
	/**
	 * 规范server路径
	 * @param serverPath
	 * @return
	 */
	private String getPath(String serverPath){
		if(isChangeDir){
			if(serverPath.startsWith("/")){
				serverPath=serverPath.substring(1, serverPath.length());
			}
		}else{
			if(!serverPath.startsWith("/")){
				serverPath="/"+serverPath;
			}
		}
		return serverPath;
	}
	
	/**
	 * 从ftp服务器上删除一个文件  
	 * 
	 * @param delFile
	 * @return
	 * @throws FTPClientException
	 */
	public boolean delete(String delFile) throws FTPClientException{
		FTPClient ftpClient=null;
		try {
			ftpClient=getFtpClient();
			ftpClient.deleteFile(delFile);
		} catch (IOException e) {
			throw new FTPClientException("Couldn't delete file from server ." , e);
		} finally{
			if(null != ftpClient){
				disconnect(ftpClient);//断开连接 
			}
		}
		return false;
	}
	
	/**
	 * 批量删除服务器文件
	 * @param delFiles
	 * @return
	 * @throws FTPClientException
	 */
	public boolean delete(String[] delFiles) throws FTPClientException{
		FTPClient ftpClient=null;
		try {
			ftpClient=getFtpClient();
			for(String delFile:delFiles){
				ftpClient.deleteFile(delFile);
			}
			return true;
		} catch (IOException e) {
			throw new FTPClientException("Couldn't delete file from server ." , e);
		} finally{
			if(null != ftpClient){
				disconnect(ftpClient);//断开连接  
			}
		}
	}
	
	/**
	 * 列出远程默认目录下所有的文件名 
	 * 
	 * @return 远程默认目录下所有文件名的列表，目录不存在或者目录下没有文件时返回0长度的数组  
	 * @throws FTPClientException 
	 */
	public String[] listNames() throws FTPClientException{
		return listNames(null);
	}
	
	/**
	 * 列出远程目录下所有的文件名
	 * 
	 * @param remotePath 远程目录名 
	 * @return 远程目录下所有文件名的列表，目录不存在或者目录下没有文件时返回0长度的数组 
	 * @throws FTPClientException
	 */
	public String[] listNames(String remotePath) throws FTPClientException {
		FTPClient ftpClient=null;
		try {
			ftpClient=getFtpClient();
			String[] listNames=ftpClient.listNames(remotePath);
			return listNames;
		} catch (IOException e) {
			throw new FTPClientException("list server files exception ", e);
		} finally{
			if(null != ftpClient){
				disconnect(ftpClient);//断开连接  
			}
		}
	}
	
	/**
	 * 获取远程服务器指定目录下的所有文件
	 * @param remotePath
	 * @return
	 * @throws FTPClientException 
	 */
	public FTPFile[] listFiles(String remotePath) throws FTPClientException{
		FTPClient ftpClient=null;
		FTPFile[] listFiles=null;
		try {
			ftpClient=getFtpClient();
			listFiles=ftpClient.listFiles(remotePath);
		} catch (FTPClientException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally{
			if(null!=ftpClient){
				disconnect(ftpClient);
			}
		}
		return listFiles;
	}
	
	public String getFileStr(String serverFile, String separator){
		StringBuilder sb=new StringBuilder();
		ByteArrayOutputStream baos=null;
		BufferedReader br=null;
		try {
			baos=new ByteArrayOutputStream();
			if(get(serverFile, baos, false)){
				br=new BufferedReader(new InputStreamReader(new ByteArrayInputStream(baos.toByteArray())));
				String msg=null;
				while((msg=br.readLine())!=null){
					sb.append(msg+separator);
				}
			}
		} catch (FTPClientException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally{
			try {
				if(baos!=null){
					baos.close();
				}
				if(br!=null){
					br.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return sb.toString();
	}
	public static void main(String[] args) throws FTPClientException, IOException {
		FtpUtil ftp=new FtpUtil("192.168.198.132","liujian","liujian");
		ByteArrayOutputStream baos=new ByteArrayOutputStream();
		if(ftp.get("/home/liujian/liujian/test/doc.xml", baos, false)){
			byte[] bt=new byte[2048];
			ByteArrayInputStream bais=new ByteArrayInputStream(baos.toByteArray());
			bais.read(bt);
			System.out.println(new String(bt));
		}
	}
}
